package com.uuah.server.endpoint;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.uuah.config.i18n.CoreMessages;
import com.uuah.config.i18n.Message;
import com.uuah.exception.UuahException;
import com.uuah.exception.UuahRuntimeException;
import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.UuahServer;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.endpoint.IEndpointBuilder;
import com.uuah.server.api.endpoint.IEndpointURI;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.api.transformer.IBaseTransformer;
import com.uuah.server.api.transport.IConnector;
import com.uuah.server.api.transport.retry.RetryPolicyTemplate;
import com.uuah.server.api.transport.service.ITransportServiceDescriptor;
import com.uuah.server.config.Constants;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.config.registry.ServiceDescriptorFactory;
import com.uuah.server.exception.endpoint.EndpointException;
import com.uuah.server.exception.registry.ServiceException;
import com.uuah.server.exception.transport.service.TransportFactoryException;
import com.uuah.server.transformer.TransformerUtils;
import com.uuah.server.transport.service.TransportFactory;
import com.uuah.server.util.UuahServerUtils;
import com.uuah.utils.CharSetUtils;
import com.uuah.utils.ClassUtils;
import com.uuah.utils.MapCombiner;

/**
 * Abstract endpoint builder used for externalizing the complex creation logic
 * of endpoints out of the endpoint instance itself. <br/>
 * The use of a builder allows i) Endpoints to be configured once and created in
 * a repeatable fashion (global endpoints), ii) Allow for much more
 * extensibility in endpoint creation for transport specific endpoints,
 * streaming endpoints etc.<br/>
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午01:36:18
 * @version 1.0.0
 */
public class AbstractEndpointBuilder implements IEndpointBuilder {

	protected URIBuilder uriBuilder;
	protected IConnector connector;
	protected IBaseTransformer requestTransformer;
	protected IBaseTransformer responseTransformer;
	protected String name;
	protected Map properties = new HashMap();
	protected Boolean synchronous;
	protected Boolean ipFilter;
	protected String initialState = IUuahEndpoint.INITIAL_STATE_STARTED;
	protected String encoding;
	protected Integer createConnector;
	protected RetryPolicyTemplate retryPolicyTemplate;

	protected String registryId = null;
	protected IUuahContext uuahContext;

	public IUuahEndpoint buildRequestEndpoint() throws EndpointException,
			InitialisationException {
		return doBuildRequestEndpoint();
	}

	protected void setPropertiesFromProperties(Map properties) {
		synchronous = getBooleanProperty(properties,
				Constants.SYNCHRONOUS_PROPERTY, synchronous);
		ipFilter = getBooleanProperty(properties,
				Constants.SYNCHRONOUS_PROPERTY, ipFilter);
	}

	public static Boolean getBooleanProperty(Map properties, String name,
			Boolean dflt) {
		if (properties.containsKey(name)) {
			return Boolean.valueOf((String) properties.get(name));
		} else {
			return dflt;
		}
	}

	public static Integer getIntegerProperty(Map properties, String name,
			Integer dflt) {
		if (properties.containsKey(name)) {
			return Integer.decode((String) properties.get(name));
		} else {
			return dflt;
		}
	}

	protected IUuahEndpoint doBuildRequestEndpoint()
			throws InitialisationException, EndpointException {
		Map properties = getProperties();
		setPropertiesFromProperties(properties);

		if (uriBuilder == null) {
			throw new UuahRuntimeException(CoreMessages
					.objectIsNull("uriBuilder"));
		}

		IEndpointURI endpointURI = uriBuilder.getEndpoint();
		endpointURI.initialise();

		IConnector connector = getConnector();

		if (connector != null && endpointURI != null
				&& !connector.supportsProtocol(endpointURI.getFullScheme())) {
			throw new IllegalArgumentException(ServerMessages
					.connectorSchemeIncompatibleWithEndpointScheme(
							connector.getProtocol(), endpointURI).getMessage());
		}

		IBaseTransformer requestTransformer = getRequestTransformer(connector,
				endpointURI);
		IBaseTransformer responseTransformer = getRequestEndpointResponseTransformer(
				connector, endpointURI);

		boolean synchronous = getSynchronous(connector, endpointURI);
		boolean ipFilter = getIpFilter(connector, endpointURI);

		return new RequestEndpoint(connector, endpointURI, requestTransformer,
				responseTransformer, getName(endpointURI), getProperties(),
				synchronous, ipFilter, getInitialState(connector),
				getEndpointEncoding(connector), name, uuahContext,
				getRetryPolicyTemplate(connector));
	}

	protected boolean getSynchronous(IConnector connector,
			IEndpointURI endpointURI) {
		return synchronous != null ? synchronous.booleanValue()
				: getDefaultSynchronous(connector, endpointURI.getScheme());
	}

	protected boolean getIpFilter(IConnector connector, IEndpointURI endpointURI) {
		return ipFilter != null ? ipFilter.booleanValue() : false;
	}

	protected boolean getDefaultSynchronous(IConnector connector,
			String protocol) {
		if (connector != null && connector.isSyncEnabled(protocol)) {
			return true;
		} else {
			return uuahContext.getConfiguration()
					.isDefaultSynchronousEndpoints();
		}
	}

	protected RetryPolicyTemplate getRetryPolicyTemplate(IConnector connector) {
		return retryPolicyTemplate != null ? retryPolicyTemplate : connector
				.getRetryPolicyTemplate();
	}

	protected IConnector getConnector() throws EndpointException {
		return connector != null ? connector : getDefaultConnector();
	}

	protected IConnector getDefaultConnector() throws EndpointException {
		return getConnector(uriBuilder.getEndpoint(), uuahContext);
	}

	protected String getName(IEndpointURI endpointURI) {
		return name != null ? name : UuahServerUtils
				.getEndpointName(endpointURI);
	}

	protected Map getProperties() {

		LinkedList maps = new LinkedList();
		if (null != uriBuilder) {
			maps.addLast(uriBuilder.getEndpoint().getParams());
		}
		if (properties != null) {
			maps.addLast(properties);
		}
		MapCombiner combiner = new MapCombiner();
		combiner.setList(maps);
		return Collections.unmodifiableMap(combiner);
	}

	protected String getEndpointEncoding(IConnector connector) {
		return encoding != null ? encoding
				: getDefaultEndpointEncoding(connector);
	}

	protected String getDefaultEndpointEncoding(IConnector connector) {
		if (uuahContext != null) {
			return uuahContext.getConfiguration().getDefaultEncoding();
		} else {
			return CharSetUtils.defaultCharsetName();
		}
	}

	protected String getInitialState(IConnector connector) {
		return initialState != null ? initialState
				: getDefaultInitialState(connector);

	}

	protected String getDefaultInitialState(IConnector connector) {
		return IUuahEndpoint.INITIAL_STATE_STARTED;
	}

	protected IBaseTransformer getRequestTransformer(IConnector connector,
			IEndpointURI endpointURI) throws TransportFactoryException {
		// #1 Transformer set on builder
		if (requestTransformer != null) {
			return requestTransformer;
		}

		// #2 Transformer specified on uri
		requestTransformer = getTransformerFromString(endpointURI
				.getRequestTransformers());
		if (requestTransformer != null) {
			return requestTransformer;
		}

		// #3 Default Transformer
		return getDefaultRequestTransformer(connector);
	}

	protected IBaseTransformer getDefaultRequestTransformer(IConnector connector)
			throws TransportFactoryException {
		try {
			return TransformerUtils
					.getDefaultRequestTransformer(getNonNullServiceDescriptor(uriBuilder
							.getEndpoint().getSchemeMetaInfo()));
		} catch (Exception e) {
			throw new TransportFactoryException(e);
		}
	}

	protected IBaseTransformer getRequestEndpointResponseTransformer(
			IConnector connector, IEndpointURI endpointURI)
			throws TransportFactoryException {
		// #1 Transformers set on builder
		if (responseTransformer != null) {
			return responseTransformer;
		}

		// #2 Transformer specified on uri
		responseTransformer = getTransformerFromString(endpointURI
				.getResponseTransformers());
		if (responseTransformer != null) {
			return responseTransformer;
		}

		// #3 Default Connector Response Transformer
		return getDefaultResponseTransformer(connector);
	}

	protected IBaseTransformer getDefaultResponseTransformer(
			IConnector connector) throws TransportFactoryException {
		try {
			return TransformerUtils
					.getDefaultResponseTransformer(getNonNullServiceDescriptor(uriBuilder
							.getEndpoint().getSchemeMetaInfo()));
		} catch (Exception e) {
			throw new TransportFactoryException(e);
		}
	}

	private IBaseTransformer getTransformerFromString(String transformer)
			throws TransportFactoryException {
		try {
			List<IBaseTransformer> list = TransformerUtils
					.getTransformers(transformer);
			if (list != null && list.size() > 0) {
				return list.get(0);
			} else {
				return null;
			}
		} catch (UuahException e) {
			throw new TransportFactoryException(e);
		}
	}

	private ITransportServiceDescriptor getNonNullServiceDescriptor(
			String scheme) throws ServiceException {
		ITransportServiceDescriptor sd = (ITransportServiceDescriptor) UuahServer
				.getUuahContext().getRegistry().lookupServiceDescriptor(
						ServiceDescriptorFactory.PROVIDER_SERVICE_TYPE, scheme);
		if (null != sd) {
			return sd;
		} else {
			throw new ServiceException(ServerMessages
					.noServiceTransportDescriptor(scheme));
		}
	}

	private IConnector getConnector(IEndpointURI endpointURI,
			IUuahContext uuahContext) throws EndpointException {
		String scheme = uriBuilder.getEndpoint().getFullScheme();
		IConnector connector;
		try {
			if (uriBuilder.getEndpoint().getConnectorName() != null) {
				connector = uuahContext.getRegistry().lookupConnector(
						uriBuilder.getEndpoint().getConnectorName());
				if (connector == null) {
					throw new TransportFactoryException(ServerMessages
							.objectNotRegistered("Connector", uriBuilder
									.getEndpoint().getConnectorName()));
				}
			} else {
				connector = TransportFactory.getConnectorByProtocol(scheme);
				if (connector == null) {
					connector = TransportFactory.createConnector(endpointURI,
							uuahContext);
					uuahContext.getRegistry().registerConnector(connector);
				}
			}
		} catch (Exception e) {
			throw new TransportFactoryException(e);
		}

		if (connector == null) {
			Message m = ServerMessages.failedToCreateObjectWith("Endpoint",
					"endpointURI: " + endpointURI);
			m.setNextMessage(CoreMessages.objectIsNull("connector"));
			throw new TransportFactoryException(m);

		}
		return connector;
	}

	// Builder setters

	public void setConnector(IConnector connector) {
		this.connector = connector;

	}

	public void setRequestTransformer(IBaseTransformer requestTransformer) {
		this.requestTransformer = requestTransformer;
	}

	public void setResponseTransformer(IBaseTransformer responseTransformer) {
		this.responseTransformer = responseTransformer;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setProperties(Map properties) {
		if (null == this.properties) {
			this.properties = new HashMap();
		}
		this.properties.putAll(properties);
	}

	public void setProperty(Object key, Object value) {
		properties.put(key, value);
	}

	public void setSynchronous(boolean synchronous) {
		this.synchronous = Boolean.valueOf(synchronous);
	}

	public void setIpFilter(boolean ipFilter) {
		this.ipFilter = Boolean.valueOf(ipFilter);
	}

	public void setInitialState(String initialState) {
		this.initialState = initialState;

	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;

	}

	public void setCreateConnector(int createConnector) {
		this.createConnector = new Integer(createConnector);
	}

	public void setRegistryId(String registryId) {
		this.registryId = registryId;

	}

	public void setUuahContext(IUuahContext uuahContext) {
		this.uuahContext = uuahContext;

	}

	public void setRetryPolicyTemplate(RetryPolicyTemplate retryPolicyTemplate) {
		this.retryPolicyTemplate = retryPolicyTemplate;

	}

	public URIBuilder getEndpointBuilder() {
		return uriBuilder;
	}

	public void setURIBuilder(URIBuilder URIBuilder) {
		this.uriBuilder = URIBuilder;

	}

	// TODO Equals() and hashCode() only needed for tests, move to tests
	public int hashCode() {
		return ClassUtils.hash(new Object[] { retryPolicyTemplate, connector,
				createConnector, encoding, uriBuilder, initialState, name,
				properties, responseTransformer, synchronous,
				requestTransformer });
	}

	// TODO Equals() and hashCode() only needed for tests, move to tests
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null || getClass() != obj.getClass())
			return false;

		final AbstractEndpointBuilder other = (AbstractEndpointBuilder) obj;
		return equal(retryPolicyTemplate, other.retryPolicyTemplate)
				&& equal(connector, other.connector)
				&& equal(createConnector, other.createConnector)
				&& equal(encoding, other.encoding)
				&& equal(uriBuilder, other.uriBuilder)
				&& equal(initialState, other.initialState)
				&& equal(name, other.name)
				&& equal(properties, other.properties)
				&& equal(responseTransformer, other.responseTransformer)
				&& equal(synchronous, other.synchronous)
				&& equal(ipFilter, other.ipFilter)
				&& equal(requestTransformer, other.requestTransformer);
	}

	protected static boolean equal(Object a, Object b) {
		return ClassUtils.equal(a, b);
	}

	public Object clone() throws CloneNotSupportedException {
		IEndpointBuilder builder = (IEndpointBuilder) super.clone();
		builder.setConnector(connector);
		builder.setURIBuilder(uriBuilder);
		builder.setRequestTransformer(requestTransformer);
		builder.setResponseTransformer(responseTransformer);
		builder.setName(name);
		builder.setProperties(properties);
		builder.setInitialState(initialState);
		builder.setEncoding(encoding);
		builder.setRegistryId(registryId);
		builder.setUuahContext(uuahContext);
		builder.setRetryPolicyTemplate(retryPolicyTemplate);
		if (synchronous != null) {
			builder.setSynchronous(synchronous.booleanValue());
		}

		if (ipFilter != null) {
			builder.setIpFilter(ipFilter.booleanValue());
		}

		return builder;
	}

}
